-- ======================================================================
-- Hell Logic Sniffer Addon 
-- 
-- Copyright 2013 Hell-Prototypes
--
-- http://www.hellprototypes.com/
-- ======================================================================

ModuleClass = "LS_DECODER"
ModuleName  = "IIC"
ModuleTip   = "To decode IIC data"


g_byte_shift_mask = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}
g_byte_iic_sample_mask = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}

g_SCL_ch_num = -1
g_SDA_ch_num = -1

g_SDA_start_table = {}
g_SDA_stop_table = {}

g_SCL_rising_edge_table = {}

g_start_out = {}
g_stop_out = {}

function main ()
    --LS_API.DebugMsg("===I2C_Decoder.lua main function called===")
    -- param check
    if type(LS_PARAM) ~= "table" then
    	LS_API.MessageBox("<IIC> LS_PARAM was not a table class")
		return
    end
    if #LS_PARAM ~= 2 then
    	LS_API.MessageBox("<IIC> LS_PARAM's size must = 5, size = " .. #LS_PARAM)
	end
	
	g_SCL_ch_num = LS_PARAM[1]
	g_SDA_ch_num = LS_PARAM[2]
	
	if g_SCL_ch_num == g_SDA_ch_num then
		LS_API.MessageBox("<IIC> SCL, SDA was at the same channel : " .. g_SCL_ch_num)
		return
	end

    g_SCL_rising_edge_table  = LS_API.GetChannelPosedgeTable(g_SCL_ch_num)
    if type(g_SCL_rising_edge_table) ~= "table" then
    	LS_API.MessageBox("<IIC>Get SCL channel data fail...")
		return
    end
    

	if #g_SCL_rising_edge_table < 9 then
		LS_API.MessageBox("<IIC>To less SCL rising edge found : " .. #g_SCL_rising_edge_table)
		return
	end
    
    creat_i2c_start_stop_table()
	if #g_SDA_start_table == 0 then
		LS_API.MessageBox("<IIC>Can't found start condition ...")
		return
	end
	if #g_SDA_stop_table == 0 then
		LS_API.MessageBox("<IIC>Can't found stop condition ...")
		return
	end

	--Analyzing
	iic_protocal_analyzing()
end
--------------------------------------------------------------------------------
function iic_protocal_analyzing()
	local l_decode_out= {}
	local l_start_out= {}
	local l_stop_out= {}
	local l_decode_out_index = 3
	local l_byte_data = 0
	local l_byte_sample_count = 1
    local l_start_bit_table_index = 1
    local l_stop_bit_table_index = 1
	local level_temp = false
	local l_bit_index = 0
	local l_table_index = 1
    local l_cycle_right_boundary = 0
    local l_cycle_repeat_start = false
	l_decode_out[1] = -1
	l_decode_out[2] = -4 -- init error flag

    while g_SDA_stop_table[l_stop_bit_table_index] < g_SDA_start_table[l_start_bit_table_index] do
        l_stop_bit_table_index = l_stop_bit_table_index + 1
    end

    while (l_start_bit_table_index <= #g_SDA_start_table) and (l_stop_bit_table_index <= #g_SDA_stop_table) do
		--start condition
        if l_start_bit_table_index <= #g_SDA_start_table then
            l_start_out[1] = g_SDA_start_table[l_start_bit_table_index]
			l_start_out[2] = -1 --start flag
            l_start_out[3] = g_SDA_start_table[l_start_bit_table_index]
			if not commit_result_to_host(l_start_out) then
				return false
			end
		end

		local temp = 0
        l_cycle_right_boundary = 0
        if l_start_bit_table_index < #g_SDA_start_table then
            temp = g_SDA_start_table[l_start_bit_table_index+1]
            if l_cycle_right_boundary < temp then
                l_cycle_right_boundary = temp
			end
		else
			if l_stop_bit_table_index <= #g_SDA_stop_table then
				l_cycle_right_boundary = g_SDA_stop_table[l_stop_bit_table_index]
			end
		end

        if l_stop_bit_table_index <= #g_SDA_stop_table then
            temp = g_SDA_stop_table[l_stop_bit_table_index]
			if l_cycle_right_boundary < temp then
                l_cycle_repeat_start = true
            else
            	l_cycle_right_boundary = temp
            end
		end

		for table_index = l_table_index,#g_SCL_rising_edge_table do
			l_bit_index = g_SCL_rising_edge_table[table_index]
            if l_bit_index > l_start_out[1] then
                if l_bit_index >= l_cycle_right_boundary then
                    l_byte_data = 0
    				l_decode_out_index = 3
    				l_byte_sample_count = 1
    				l_table_index = table_index
    				break
    			end
    			level_temp = get_SDA_bit_level(l_bit_index)
    			if l_byte_sample_count < 9 then --1 ~ 8
    				--data
    				if l_byte_sample_count == 1 then
    					l_decode_out[1] = l_bit_index
    				end

    				if level_temp then
    					l_byte_data = l_byte_data + g_byte_iic_sample_mask[l_byte_sample_count]
    				end
    				l_byte_sample_count = l_byte_sample_count + 1

    				l_decode_out[l_decode_out_index] = l_bit_index
    				l_decode_out_index = l_decode_out_index + 1
    			else
    				--ACK
    				if (level_temp == false) then
    					l_decode_out[2] = l_byte_data
    				else
    					l_decode_out[2] = -(l_byte_data + 0x100) --no ACK flag
    				end
    				l_decode_out[l_decode_out_index] = l_bit_index
    				l_byte_data = 0
    				l_decode_out_index = 3
    				l_byte_sample_count = 1
    				if not commit_result_to_host(l_decode_out) then
    					return false
    				end
    			end
            end
		end
		
        if not l_cycle_repeat_start then
            --stop condition
            if l_stop_bit_table_index <= #g_SDA_stop_table then
                l_stop_out[1] = g_SDA_stop_table[l_stop_bit_table_index]
                l_stop_out[2] = -2 --stop flag
                l_stop_out[3] = g_SDA_stop_table[l_stop_bit_table_index]
                if not commit_result_to_host(l_stop_out) then
                    return false
                end
            end
            l_stop_bit_table_index = l_stop_bit_table_index + 1
        end

        l_cycle_repeat_start = false
        l_start_bit_table_index = l_start_bit_table_index + 1
	end
end
--------------------------------------------------------------------------------
function creat_i2c_start_stop_table()
    local l_SDA_index = 1
    local l_count = 1
    local bit_index = 0
    
    local l_SDA_falling_edge_table = LS_API.GetChannelNegedgeTable(g_SDA_ch_num)
    if type(l_SDA_falling_edge_table) ~= "table" then
    	LS_API.MessageBox("<IIC>Get SDA channel data fail...")
		return
    end

    while l_SDA_index <= #l_SDA_falling_edge_table do
		bit_index = l_SDA_falling_edge_table[l_SDA_index]
		level = get_SCL_bit_level(bit_index)
		if level then
            g_SDA_start_table[l_count] = bit_index
			l_count = l_count + 1
			--LS_API.DebugMsg("<IIC>Start Condition: " .. bit_index)
		end
		l_SDA_index = l_SDA_index + 1
	end

    l_SDA_index = 1
    l_count = 1
	local l_SDA_rising_edge_table  = LS_API.GetChannelPosedgeTable(g_SDA_ch_num)
    if type(l_SDA_rising_edge_table) ~= "table" then
    	LS_API.MessageBox("<IIC>Get SDA channel data fail...")
		return
    end
    while l_SDA_index <= #l_SDA_rising_edge_table do
		bit_index = l_SDA_rising_edge_table[l_SDA_index]
		level = get_SCL_bit_level(bit_index)
		if level then
			g_SDA_stop_table[l_count] = bit_index
			l_count = l_count + 1
			--LS_API.DebugMsg("<IIC>Stop Condition: " .. bit_index)
		end
		l_SDA_index = l_SDA_index + 1
	end
end
--------------------------------------------------------------------------------
function get_SCL_bit_level ( bit_index )
    local level = LS_API.GetChannelLevelByIndex(g_SCL_ch_num, bit_index);
	if level == 1 then
		return true
	else
		return false
    end
end
--------------------------------------------------------------------------------
function get_SDA_bit_level( bit_index )
    local level = LS_API.GetChannelLevelByIndex(g_SDA_ch_num, bit_index);
	if level == 1 then
		return true
	else
		return false
    end
end
--------------------------------------------------------------------------------
function commit_result_to_host(value)
	ret = LS_API.DecodeElementPushBack(value)
	if ret ~= 0 then
		LS_API.MessageBox("<IIC> Fail to push decode result to host... ret =" .. ret)
		return false
	else
		--LS_API.DebugMsg("=== g_byte_data = ".. g_byte_data)
	end
	return true
end
